package com.google.code.krillion.firehose.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.code.krillion.firehose.data.AvailabilityStat;
import com.google.code.krillion.firehose.data.PMF;
import com.google.code.krillion.firehose.data.TweetStat;

import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.GeoLocation;
import twitter4j.http.HttpClient;
import twitter4j.http.HttpResponse;

public class AvailabilityWorkerServlet implements Servlet {
    private static Logger log = Logger.getLogger(AvailabilityWorkerServlet.class.getName());
	private static ServletConfig config;
	private static Twitter twitter;
	private static Map<String, Twitter> cityhoseMap;
	private static String apiCall;
	private static String tweetMessage;
    private static final String CITYHOSE_KEY_PROP = "cityhose.key.";  
    private static final String CITYHOSE_VALUE_PROP = "cityhose.value.";  
	
	@Override
	public void destroy() {
		config = null;
		apiCall = null;
	}

	@Override
	public ServletConfig getServletConfig() {
		return config;
	}

	@Override
	public String getServletInfo() {
		return null;
	}

	@Override
	public void init(ServletConfig sc) throws ServletException {
		config = sc;
		ResourceBundle bundle = ResourceBundle.getBundle("com.google.code.krillion.firehose.server.krillion");
		final String username = (String) bundle.getObject("firehose.username");
		final String password = (String) bundle.getObject("firehose.password");
		if (username != null && password != null) {
			TwitterFactory factory = new TwitterFactory();
		    twitter = factory.getInstance(username,password);
			try {
				twitter.verifyCredentials();
			} catch (TwitterException e) {
				log.severe("TwitterException verifying user credentials: " + e.getMessage());
			}
			// Load map of zip keys to cityhose twitter instances
			int i = 1;;
			try {
				String key, value;
				cityhoseMap = new HashMap<String, Twitter>();
				while (  (key = (String) bundle.getObject(CITYHOSE_KEY_PROP + i)) != null
					  && (value = (String) bundle.getObject(CITYHOSE_VALUE_PROP + i)) != null ) {
			        log.info(CITYHOSE_KEY_PROP + i + " = " + key);
			        log.info(CITYHOSE_VALUE_PROP + i + " = " + value);
					Twitter cityhose = factory.getInstance(value, password);
					cityhoseMap.put(key, cityhose);
					i++;
				}
			} catch (MissingResourceException mre) {
				log.info("Init: Successfully loaded " + --i + " cityhose twitter accounts.");
			}
		}
		apiCall = (String) bundle.getObject("krillion.api.call.sku");
		tweetMessage = (String) bundle.getObject("tweet.available");
	}

	@Override
	public void service(ServletRequest req, ServletResponse resp)
	throws ServletException, IOException {
		String zip   = req.getParameter("zip");
		String brand = req.getParameter("brand");
		String sku   = req.getParameter("sku");
		log.info("zip:" + zip + ", brand:" + brand + ", sku:" + sku);
		resp.setContentType("text/plain");
		PrintWriter writer = resp.getWriter();
        writer.println("zip:" + zip + ", brand:" + brand + ", sku:" + sku);
		try {
			Object[] args = {sku, brand, zip};
			final String url = MessageFormat.format(apiCall, args);
			HttpClient client = new HttpClient();
			HttpResponse response = client.get(url);
	//		final String responseStr = response.asString();
	//		log.info("responseAsString: " + responseStr);
			Document dom = response.asDocument();
			NodeList productList = dom.getElementsByTagName("product");
			NodeList titleList   = dom.getElementsByTagName("title");
			NodeList priceList = dom.getElementsByTagName("bestAvailablePrice");
			if (  productList.getLength() > 0
			   && titleList.getLength() > 0
			   && priceList.getLength() > 0) {
				final Node titleNode = titleList.item(0);
				final Node priceNode = priceList.item(0);
				final Node retailerNode = priceList.item(0).getAttributes().getNamedItem("retailer");
				final Node cityNode = priceList.item(0).getAttributes().getNamedItem("city");
				final Node stateNode = priceList.item(0).getAttributes().getNamedItem("state");
				final Node linkNode = productList.item(0).getAttributes().getNamedItem("url");
				final Node latitudeNode = priceList.item(0).getAttributes().getNamedItem("latitude"); 
				final Node longitudeNode = priceList.item(0).getAttributes().getNamedItem("longitude"); 
				if (  titleNode != null
			       && priceNode != null
			       && retailerNode != null
			       && cityNode != null
			       && stateNode != null
				   && linkNode != null
				   && latitudeNode != null
				   && longitudeNode != null ) {					
					final String title = titleNode.getTextContent();
					final String price = priceNode.getTextContent();
					final String retailer = retailerNode.getTextContent();
					final String city  = cityNode.getTextContent();
					final String state  = stateNode.getTextContent();
					final String link  = linkNode.getTextContent();
					final double latitude = Double.parseDouble(latitudeNode.getTextContent());
					final double longitude = Double.parseDouble(longitudeNode.getTextContent());
					final GeoLocation location = new GeoLocation(latitude, longitude);
					Object[] tweetArgs = {title, retailer, city, price, link};
					final String tweet = MessageFormat.format(tweetMessage, tweetArgs);
					twitter.updateStatus(tweet, location);
					// Persist tracking data for available products in datastore
					PersistenceManager pm = PMF.get().getPersistenceManager();
					try {
						AvailabilityStat availabilityStat = new AvailabilityStat(
								zip, brand, sku, true );
			            pm.makePersistent(availabilityStat);
			            
						TweetStat tweetStat = new TweetStat(
								zip, brand, sku, title, retailer, city, state,
								price, link, latitude, longitude );
			            pm.makePersistent(tweetStat);
					} finally {
			            pm.close();
					}
					// Check to see whether there is a cityhose that gets this update also
					Twitter cityhose = cityhoseMap.get(zip);
					if (cityhose != null) {
						cityhose.updateStatus(tweet, location);
						log.info("cityhose/" + zip + ": " + tweet);
						writer.println("cityhose/" + zip + ": " + tweet);
					} else {
						log.info("firehose/" + zip + ": " + tweet);
						writer.println("firehose/" + zip + ": " + tweet);
					}
				} else {
					log.severe("extracting availability result DOM nodes failed");
					writer.println("extracting availability result DOM nodes failed");
				}
			} else {
				if (  productList.getLength() > 0
				   && titleList.getLength() > 0	) {
					log.info("UNAVAILABLE: product not available locally for zip:" + zip + ", brand:" + brand + ", sku:" + sku);
					// Persist tracking data for unavailable products in datastore
					PersistenceManager pm = PMF.get().getPersistenceManager();
					try {
						AvailabilityStat availabilityStat = new AvailabilityStat(
								zip, brand, sku, false );
			            pm.makePersistent(availabilityStat);
					} finally {
			            pm.close();
					}
				} else {
					final String responseStr = response.asString();
					log.severe("extracting availability result NodeLists from Krillion response failed:\n\n" + responseStr);
					writer.println("extracting availability result NodeLists from Krillion response failed:\n\n" + responseStr);
				}
			}
			
		} catch (TwitterException e) {
			log.severe("TwitterException processing availability queue task: " + e.getMessage());
			writer.println("TwitterException processing availability queue task: " + e.getMessage());
		}
		writer.flush();
		writer.close();
	}
	
}
